package com.zycfc.zsf.boot.util.net;

import javax.net.*;
import com.zycfc.zsf.boot.util.bean.*;
import java.net.*;
import java.util.*;
import com.zycfc.zsf.boot.util.text.*;
import org.slf4j.*;

public abstract class NetUtils
{
    private static final Logger log;
    public static final int PORT_RANGE_MIN = 1024;
    public static final int PORT_RANGE_MAX = 65535;
    private static final String IP_PATTERN = "(\\d{1,3}\\.){3}\\d{1,3}";
    private static final String COLON_SEPARATOR = ":";
    private static final Random RANDOW;
    private static InetAddress localAddress;
    private static String localHost;
    private static volatile String cachedLocalHostIp;
    
    public static InetAddress getLocalAddress() {
        return NetUtils.localAddress;
    }
    
    public static String getLocalHost() {
        return NetUtils.localHost;
    }
    
    public static boolean isPortAvailable(final int port) {
        try {
            final ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(port, 1, InetAddress.getByName("localhost"));
            serverSocket.close();
            return true;
        }
        catch (Exception ex) {
            return false;
        }
    }
    
    public static int findRandomAvailablePort() {
        return findRandomAvailablePort(1024, 65535);
    }
    
    public static int findRandomAvailablePort(final int minPort, final int maxPort) {
        final int portRange = maxPort - minPort;
        int searchCounter = 0;
        while (++searchCounter <= portRange) {
            final int candidatePort = minPort + NetUtils.RANDOW.nextInt(portRange + 1);
            if (isPortAvailable(candidatePort)) {
                return candidatePort;
            }
        }
        throw new IllegalStateException(String.format("Could not find an available tcp port in the range [%d, %d] after %d attempts", minPort, maxPort, searchCounter));
    }
    
    public static int findAvailablePortFrom(final int minPort) {
        for (int port = minPort; port < 65535; ++port) {
            if (isPortAvailable(port)) {
                return port;
            }
        }
        throw new IllegalStateException(String.format("Could not find an available tcp port in the range [%d, %d]", minPort, 65535));
    }
    
    private static void initLocalAddress() {
        NetworkInterface nic = null;
        try {
            NetUtils.localAddress = InetAddress.getLocalHost();
            nic = NetworkInterface.getByInetAddress(NetUtils.localAddress);
        }
        catch (Exception ex) {}
        if (NetUtils.localAddress == null || nic == null || NetUtils.localAddress.isLoopbackAddress() || NetUtils.localAddress instanceof Inet6Address) {
            final InetAddress lookedUpAddr = findLocalAddressViaNetworkInterface();
            try {
                NetUtils.localAddress = ((lookedUpAddr != null) ? lookedUpAddr : InetAddress.getByName("127.0.0.1"));
            }
            catch (UnknownHostException ex2) {}
        }
        NetUtils.localHost = IPUtils.toString(NetUtils.localAddress);
    }
    
    private static InetAddress findLocalAddressViaNetworkInterface() {
        final String preferNamePrefix = SystemPropertiesUtils.getString("localhost.prefer.nic.prefix", "LOCALHOST_PREFER_NIC_PREFIX", "bond0.");
        final String defaultNicList = SystemPropertiesUtils.getString("localhost.default.nic.list", "LOCALHOST_DEFAULT_NIC_LIST", "bond0,eth0,em0,br0");
        InetAddress resultAddress = null;
        final Map<String, NetworkInterface> candidateInterfaces = new HashMap<String, NetworkInterface>();
        try {
            final Enumeration<NetworkInterface> allInterfaces = NetworkInterface.getNetworkInterfaces();
            while (allInterfaces.hasMoreElements()) {
                final NetworkInterface nic = allInterfaces.nextElement();
                try {
                    if (!nic.isUp() || !nic.supportsMulticast()) {
                        continue;
                    }
                }
                catch (SocketException e) {
                    continue;
                }
                final String name = nic.getName();
                if (name.startsWith(preferNamePrefix)) {
                    resultAddress = findAvailableInetAddress(nic);
                    if (resultAddress != null) {
                        return resultAddress;
                    }
                    continue;
                }
                else {
                    candidateInterfaces.put(name, nic);
                }
            }
            for (final String nifName : defaultNicList.split(",")) {
                NetUtils.log.info("---------nifName=" + nifName);
                final NetworkInterface nic2 = candidateInterfaces.get(nifName);
                if (nic2 == null) {
                    NetUtils.log.info("****nic****is***null");
                }
                else {
                    resultAddress = findAvailableInetAddress(nic2);
                    if (resultAddress != null) {
                        return resultAddress;
                    }
                }
            }
        }
        catch (SocketException e2) {
            return null;
        }
        return null;
    }
    
    private static InetAddress findAvailableInetAddress(final NetworkInterface nic) {
        if (nic == null) {
            NetUtils.log.info("nic-------------is***null--------");
        }
        if (nic.getInetAddresses() == null) {
            NetUtils.log.info("nic.getInetAddresses()-------------is***null--------");
        }
        final Enumeration<InetAddress> indetAddresses = nic.getInetAddresses();
        while (indetAddresses.hasMoreElements()) {
            final InetAddress inetAddress = indetAddresses.nextElement();
            if (!(inetAddress instanceof Inet6Address) && !inetAddress.isLoopbackAddress()) {
                return inetAddress;
            }
        }
        return null;
    }
    
    public static void main(final String[] args) {
        final long t = System.currentTimeMillis();
        final long l = t / 1000L;
        System.out.println(t);
        System.out.println(l);
        System.out.println(getHostIp());
    }
    
    public static String getLocalHostIp() {
        if (StringUtils.isNotEmpty(NetUtils.cachedLocalHostIp)) {
            return NetUtils.cachedLocalHostIp;
        }
        if (StringUtils.isEmpty(NetUtils.cachedLocalHostIp)) {
            synchronized (NetUtils.class) {
                if (StringUtils.isEmpty(NetUtils.cachedLocalHostIp)) {
                    NetUtils.cachedLocalHostIp = getHostIp();
                }
            }
        }
        return NetUtils.cachedLocalHostIp;
    }
    
    private static String getHostIp() {
        String hostIp = null;
        InetAddress ip = null;
        try {
            final Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
            while (netInterfaces.hasMoreElements()) {
                final NetworkInterface ni = netInterfaces.nextElement();
                final Enumeration<InetAddress> ips = ni.getInetAddresses();
                while (ips.hasMoreElements()) {
                    ip = ips.nextElement();
                    if (isPublicIpAddress(ip)) {
                        return ip.getHostAddress();
                    }
                    if (!isLocalIpAddress(ip)) {
                        continue;
                    }
                    hostIp = ip.getHostAddress();
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return hostIp;
    }
    
    private static boolean isPublicIpAddress(final InetAddress ipAddress) {
        return !ipAddress.isSiteLocalAddress() && !ipAddress.isLoopbackAddress() && !isV6IpAddress(ipAddress);
    }
    
    private static boolean isLocalIpAddress(final InetAddress ipAddress) {
        return ipAddress.isSiteLocalAddress() && !ipAddress.isLoopbackAddress() && !isV6IpAddress(ipAddress);
    }
    
    private static boolean isV6IpAddress(final InetAddress ipAddress) {
        return ipAddress.getHostAddress().contains(":");
    }
    
    static {
        log = LoggerFactory.getLogger((Class)NetUtils.class);
        RANDOW = new Random();
        initLocalAddress();
    }
}
